/**
 ff js API (c) 2005 by Sven Helmberger ( sven dot helmberger at gmx dot de )
 project homepage: http://fforw.de/post/ff_javascript_library/
 available under a revised BSD-like library
 
 @projectDescription ff javascript library
 @author Sven Helmberger ( sven dot helmberger at gmx dot de )
 @version 1.11
 @namespace ff
 */

/**
 * @classDescription ff javascript base
 */
var ff=
{
/**
 * @type {String} ffjs info field. Modified by IE compat and ff.dom
 */
info: "ffjs-1.11",
/**
 * @type {int} current wait level
 */
_wcnt: 0,
/**
 * Creates a cross-brower XMLHttpRequest object
 * @return {XMLHttpRequest} xmlhttprequest object or null on fail
 */
_createXHR:
  function()
  {
    var xhr=null;    
    if (typeof XMLHttpRequest!='undefined') 
    {
      xhr = new XMLHttpRequest();
    }    
    return xhr;
  },
/**
 * Sets or clears the wait cursor for the document body.
 * @param {boolean} wait 
 */
wait:
  function(wait)
  {
    if (wait)
    {
      ff._wcnt++;
      document.body.style.cursor="wait";
    }
    else
    {      
      if (ff._wcnt-- <= 1)
      {
        ff._wcnt=0;
        document.body.style.cursor="auto";
      }  
    }
  },
/**
 * Makes an AJAX request
 * @param {Object} config Associative array configuring the request
 * Keys: url - URL to request, response - function to call with the response,
 * error - function to call in case of error, method - "GET" or "POST", 
 * params - HTTP Parameters, body - raw body to post, waitCursor : set wait cursor  
 */
request:
  function (config)
  {
  	
  	var Response=function(xhr)
	{
		this.xhr=xhr;
		this.text=function()
		{
			return this.xhr.responseText;
		};
		this.xml=function()
		{
			return this.xhr.responseXML;
		};
		this.json=function()
		{
            eval("var __json="+this.xhr.responseText);
			return __json;
		};
		this.html=function()
		{
          var div=document.createElement("div");
          div.innerHTML=this.xhr.responseText;
          return div.childNodes;            
		};
		this.unlink=function()
		{
			this.xhr=null;
		};
		xhr=null;
	};
  	    
  	if (!config.response)
  	{
  		console.error("No response function.");
  	}
  	if (!config.url)
  	{
  		console.error("No URL to request.");
  	}
  	
  	var url=config.url;
  	var method=config.method || "GET";
  	var params=config.params;
  	  
    var body=config.body;    	    
    if (!body && params)
    {
      var key;
      if (method == "GET")
      {
        var divider=url.indexOf("?") < 0 ? "?" : "&";
        for ( key in params)
        {
          url=url+divider+key+"="+escape(params[key]);
          divider="&";
        }
      }
      else if ( method == "POST")
      {
        body="";
        for ( key in params)
        {
          if (body.length > 0)
          {
            body+="&";
          }
          body+=key+"="+escape(params[key]);
        }
      }
      else
      {        
      	console.error("Unsupported HTTP Method "+method);
      	return;
      }
    }                
    var xhr=ff._createXHR();
    if (!xhr)
    {
      console.error("no xmlhttp");    
      return null;
    }
    if (config.waitCursor)
      ff.wait(true);
    xhr.open( method, url, true);
    xhr.onreadystatechange=function()
    {    	
      try
      {
        if (xhr.readyState == 4)
        {
          var response=new Response(xhr);
          var status=xhr.status;
          if (status == 200)
          {          	
	        if (config.waitCursor)
	          ff.wait(false);
	      	var resp=config.response;
            resp(response);
          }
          else
          {
            throw "HTTP error "+status;
          }
          response.unlink();
        }
      }
      catch(e)
      {
         if (config.waitCursor)
            ff.wait(false);
         if (config.error)
         {
             config.error(response,e);
         }
         else
         {
         	console.error(e);
         }
         if (response)
		     response.unlink();
      }
    };            
    if ( method == "POST")
    {
      xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    }
    xhr.send( body);
  },
/**
 * Returns the current state of the given form element as associative array.
 * @param {DOMElement} formElement DOM form element
 * @return {Object} associative array containing the form state
 */
readParameter:
  function(formElement)
  {
    if (formElement && formElement.nodeName == "FORM")
    {
      var params=new Array();
      var elements=formElement.elements;
      for (var i=0; i < elements.length; i++)
      {
        var e=elements[i];
        if (e.name && e.name.length > 0)
        {
          params[e.name]=e.value;
        }
      }
      return params;
    }
    else
    {
      throw "no form";
    }
  },
/**
 * Calls the given function for every dom element of the given type having the given
 * classname. If the top element is given, only dom elements below that are checked.
 * @param {String} name tag type to search
 * @param {String} className class name to search
 * @param {Function} fn function to call 
 * @param {DOMElement} [top] Top DOM element
 */
forElement:
  function( name, className, fn, top)
  {
    var elements=(top || document).getElementsByTagName( name);  
    var re=ff.style._re(className);
    for (var i = 0; i < elements.length; i++)
    {
      var e=elements[i];
      if (re.test(ff.style.getClass(e)))
      {
        fn(e);
      } 
    } 
  },
/**
 * Returns true if the browser supports XMLHttpRequest, and basic DOM operations.
 * @return {boolean} supports?
 */
hasCapabilities:
  function ()
  {
    var xhr=ff._createXHR();
    return (xhr != null && document.createElement && document.getElementById);
  },
/**
 * Extends an object with the attributes of another object.
 * The object that gets extended receives all attributes from the source object,
 * overwriting attributes if they already exist.
 * @param {Object} object that gets extended
 * @param {Object} extension attribute source object 
 */
extend:
    function(obj,extension)
    {
        for (var k in extension)
        {
            var v=extension[k];
            if (v)
            {
                obj[k]=v;
            }
        }
        return obj;
    },
/**
 * Contains the DOM related functions of the ff javascript library
 */
dom:
    {
    /**
     * Removes all children from the given DOMElement
     * @param {DOMElement} elem DOM element
     */        
    clear:
      function ( elem)
      {
        while (elem.hasChildNodes())
          elem.removeChild(elem.firstChild);    
      },  
    /**
     * Replaces the current children of the DOM element with the given child(ren)
     * @param {DOMElement} elem DOM element
     * @param {DOMElement,Array} The new child/children
     */
    replace:
    	function(elem, newChildren)
    	{
    		ff.dom.clear(elem);
    		ff.dom.append(elem, newChildren);
    	},
    /**
     * Appends the given child(ren) to the children of the given DOM element
     * @param {DOMElement} e DOM element
     * @param {DOMElement,Array} c child/children to append
     */
    append:
    	function(e, c)
    	{					
    		if (c.length)
    		{
    			while(c.length)
    			{
    				e.appendChild(c[0]);
    			}
    		}
    		else
    		{
    			e.appendChild(c);
    		}
    	},
    /**
     * Creates a DOM creation function for every tag type
     * @param {Object} domBase base object which the DOM creation functions are properties of. 
     */
    init:
    	function(domBase)
    	{
    		domBase = domBase || ff.dom;
            if (domBase.VAR)
                return;
                
    		var createDOMFn=function(name)
    		{
    			return function()
    			{
    				return ff.dom.element(name,arguments);
    			};
    		};
    	
    		var elems="A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR".split(" ");
    		for (var i=0; i < elems.length; i++)
    		{
    			var elem=elems[i];
    			domBase[elem]=createDOMFn(elem);
    		}
    		window.console.info("init DOM");
    	},
    /**
     * Generic DOM creation function. This function always exists, even when ff.dom.init is not called
     * @param {String} name Tag name of the DOM element 
     * @param {Array} child Array containing the children of the new DOM element. Can be either DOM elements or strings or numbers
     * @see ff.dom#init
     */
    element:
    	function(name, child)
    	{	
    		child=child || [];
    		var i=0;		
    		var attr={};
    		if (child.length > 0 && !child[0].appendChild && typeof child[0] == "object")
    	    {
    			attr=child[0];
				i++;
    		}
    		
            return this._el2(name,i,child,attr);    		
    	},
    _el2: function(name,i,child,attr)
    {
		var elem=document.createElement(name);

		for (var m in attr)
		{
			if (typeof m != "function")
			{
                this.setAttribute(elem,m,attr[m]);
			}
		}

		while( i < child.length)
		{
			var c=child[i++];
            if (c)
            {
    			if (c.appendChild)
    			{
    				elem.appendChild(c);
    			}
    			else
    			{
    				elem.appendChild(document.createTextNode(new String(c).valueOf()));
    			}
            }
		}
		return elem;        
    },
    /**
     * Sets the given attribute on the given DOM element to the given value.
     * @param {DOMElement} e DOM element 
     * @param {String} a attribute name
     * @param {String} v value
     */
    setAttribute:
        function(e,a,v)
        {
            e.setAttribute(a,v);       
        },
    /**
     * Returns the value of the given attribute of the given DOM element
     * @param {DOMElement} e DOM element 
     * @param {String} a attribute name
     */
    getAttribute:
        function(e,a)
        {
            return e.getAttribute(a);       
        }              
    },
/**
 * Style class related functions of the ff javascript library.
 */
style:
    {
    /**
     * Sets all class names for the given DOM element.
     * @param {DOMElement} elem DOM element
     * @param {String} class names separated by spaces
     */        
    setClass:
      function(elem, c)
      {
        ff.dom.setAttribute(elem,"class", c);
      },
    /**
     * Returns all class names of the given DOM element
     * @param {DOMElement} elem DOM element 
     * @return {String} class names separated by spaces
     */
    getClass:
      function(elem)
      {
        return ff.dom.getAttribute(elem,"class");
      },
    /**
     * Adds a class name to the class names of the given DOM element.
     * @param {DOMElement} elem DOM element
     * @param {String} c class name to add
     */
    addClass:
      function (elem, c)
      {
        var klass=ff.style.getClass(elem);
        if (klass)
        {
          klass+=" "+c;
        }
        else
        {
          klass=c;
        }
        ff.style.setClass(elem, klass);
      },
    /**
     * Removes all occurences of teh given class name from the class names of the given DOM element
     * @param {DOMElement} elem DOM element
     * @param {String} c class name to remove
     */
    removeClass:
      function (elem, c)
      {    
        var klass=ff.style.getClass(elem);
        if (klass)
        {
          var newClass="";
          var classes=klass.split(" ");
          for (var i=0; i < classes.length; i++)
          {
            var curClass=classes[i];
            if (curClass != c)
            {
              if (newClass.length > 0)
              {
                newClass+=" ";
              }
              newClass+=curClass;
            }
          }
          ff.style.setClass(elem, newClass);
        }
      },
    _re:
        function(c)
        {
           return new RegExp("\\b"+c+"\\b");
        },
    hasClass:
        function(el,c)
        {
            return this._re(c).test(this.getClass(el));
        },
    toggleClass:
      function(el,c)
      {
          if (this.hasClass(el,c))
          {
              this.removeClass(el,c);
              return false;
          }
          else
          {
              this.addClass(el,c);
              return true;
          }
      }
   },
/**
 * Contains all event related functions of the ff javascript library.
 * Based on John Resig's winning entry to the addEvent recoding contest ( http://ejohn.org/projects/flexible-javascript-events/ ) 
 */
event:
    {
    /**
     * Adds an event handler to the given DOM element
     * @param {DOMElement} obj DOM element
     * @param {String} type Event type to handle ("click" , "focus" etc) 
     * @param {Function} fn Event Handler function.
     */
    add:
      function ( obj, type, fn )
      {
        obj.addEventListener( type, fn, false );
      },
    /**
     * Removes an event handler from the given DOM element
     * @param {DOMElement} obj DOM element
     * @param {String} type Event type to handle ("click" , "focus" etc) 
     * @param {Function} fn Event Handler function.
     */
    remove:
      function ( obj, type, fn )
      {
        obj.removeEventListener( type, fn, false );
      },
    /**
     * Adds a function to call when the page is loaded. Supports multiple functions.
     * @param {DOMElement} obj DOM element
     * @param {String} type Event type to handle ("click" , "focus" etc) 
     * @param {Function} fn Event Handler function.
     */
    onload:
      function(func)
      {
        var oldonload = window.onload;
        if (typeof window.onload != "function")
        {
          window.onload = func;
        }
        else
        {
          window.onload = function()
          {
            oldonload();
            func();
          }
        }   
      },
    /**
     * Prevents the giving event from bubbling up
     * @param {Event} e
     */
    noBubble:
      function(e)
      {
        e.stopPropagation();
      },
    /**
     * Prevents the default handling of the given event
     * @param {Event} e
     */
    preventDefault:
      function(e)
      {
        e.preventDefault();
      }
    }
};
// IE compat code
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
ff.__cxhr=ff._createXHR;
ff.progIds=['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
ff._createXHR=function()
{
    var xhr=ff.__cxhr();
    for (var i=0; !xhr && i < ff.progIds.length; i++)
    {
	    try
	    {
	      xhr = new ActiveXObject(ff.progIds[i]);
	      ff.progIds=[ff.progIds[i]];
	    }
	    catch (e)
	    {
	    	console.error(e);
	    }
	 }

    return xhr;
};

ff.event.add=function ( obj, type, fn )
{
    obj['e'+type+fn] = fn;
    obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
    obj.attachEvent( 'on'+type, obj[type+fn] );
};

ff.event.remove=function ( obj, type, fn )
{
    obj.detachEvent( "on"+type, obj[type+fn] );
    obj[type+fn] = null;
    obj["e"+type+fn] = null;
};

ff.event.noBubble=
function(e)
{
    window.event.cancelBubble = true;
};

ff.event.preventDefault=function(e)
{
    window.event.returnValue=false;
    return false;
};

ff.dom._ar=
{
    "class": "className",
    "checked": "defaultChecked",
    "usemap": "useMap",
    "for": "htmlFor"
};

ff.dom.setAttribute=function(e,a,v)
{
    var a2=this._ar[a]; 
    if (a2)
        e.setAttribute(a2,v);
    else if (a == "style")
        e.style.cssText=v;
    else if (a.substring(0,2) == "on") 
        e[a] = new Function(v);
    else
        e.setAttribute(a,v);
};
    
ff.dom.getAttribute=function(e,a)
{
    var a2=this._ar[a]; 
    if (a2)
        return e.getAttribute(a2);
    else        
        return e.getAttribute(a);
};

    
ff.dom.__el2=ff.dom._el2;
ff.dom._el2=function(n,i,c,a)
{
    if (i < c.length && n == "TEXTAREA")
    {
        a.value=c[i];
        // only text is valid inside a textarea
        c=[];
    }

    var n2="<"+ n;
    if (a.name)
        n2 += " name=\""+ a.name +"\"";
    if (a.tabindex)
        n2 += " tabindex=\""+ a.tabindex +"\"";
    if (n == "INPUT" && a.type)
        n2 += " type=\""+ a.type +"\"";
    n2 += ">";
    return this.__el2(n2,i,c,a);
};

ff.info+=" ( IE compat )";
@end @*/

(function()
{
        
    var query = window.location.search;
	var debug = query && query.indexOf("ffdebug") >= 0;
	// emulate firebug console if firebug is not installed
	if (!window.console || debug)
	{
		window.console={};
		var nopFn=function(){};
        var createFns=function(a,p)
        {
    		for (var i=0; i < a.length; i++)
    		{
    			window.console[p+a[i]]=nopFn;
    		}            
        };
        createFns("debug info warn error trace time timeEnd count".split(" "), "");
        var a="x Equals NotEquals Greater NotGreater Less NotLess Contains NotContains True False Null NotNull Undefined NotUndefined InstanceOf NotInstanceOf TypeOf NotTypeOf".split(" ");
        a[0]="";
        createFns(a,"assert");

	    if (debug)
	    {
	    	ff.event.onload(function()
	    	{
				var logFn=function(name)
				{
					window.console[name]=function()
					{
						return window.console._log(name,arguments);
					};
				};
		    	window.console._log=function(name,args)
		    	{
		    		msg=args[0];
		    		var pos=1;
		    		while (pos < args.length)
		    		{
		    			msg=msg.replace(/\%[0-9\.a-zA-Z]/, args[pos++]);
		    		}
		    		window.console._container.appendChild(
		    			ff.dom.element("DIV", [{"class":name},msg]));
		    	};

                var container=ff.dom.element("DIV", [{"id":"ffdebug"}]);
		    	window.console._container=container;
		    	document.body.appendChild(window.console._container);
                ff.event.add(container, "dblclick", function(ev)
                {
                    ff.style.toggleClass(this,"minimized");
                    ff.event.preventDefault(ev);
                });
		    	logFn("debug");
		    	logFn("info");
		    	logFn("warn");
		    	logFn("error");
		    	window.console.info(ff.info);
			});
		}
	}
	else
	{
		window.console.info(ff.info);
	}
})();
